Skip to content

Conversation

@dividedmind
Copy link
Contributor

No description provided.

dividedmind and others added 21 commits January 15, 2026 04:21
… leaks

The fastjson JSONWriter.close() method does not close the underlying
Writer passed to its constructor - it only closes the internal
SerializeWriter, which has no effect. This caused FileOutputStream and
OutputStreamWriter instances to leak in RecordingSession.

Changes:
- Make AppMapSerializer implement AutoCloseable and track the underlying
  Writer to close it explicitly
- Add try-with-resources for AppMapSerializer in checkpoint() to ensure
  proper cleanup on exceptions
- Add try-finally in stop() to ensure serializer is closed even if
  write() fails
- Consolidate JSON finalization and closing logic into close() method,
  removing the redundant finish() method

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The code previously used 9090 which is memorable enough to conflict
with other code that could be running on developer machines.
New default of 46406 is unlikely to hit such conflicts.

Also, some small improvements in the httpcore bats script.
Upgrades the project to use Gradle 9.2.1, allowing the build to run on
Java 21 environments while maintaining Java 8 bytecode compatibility.

Key changes:
- Upgrade Gradle Wrapper to 9.2.1.
- Update CI workflows to use Java 21.
- Set `options.release = 8` to ensure Java 8 compatibility (replaces
  obsolete source/target compatibility).
- content: upgrade `shadow` plugin to 9.3.1 and `nexus-publish` to 2.0.0.
- Fix Gradle 9 deprecation warnings (property assignment syntax).
- Fix `ShadowRelocation` task compatibility with new Gradle API.
- Bump Gradle version to 8.5 in some test code incompatible with 9.
- Make smoke tests (event, access) self-contained and directory-agnostic,
  removing the need for 'gradlew testClasses' during test installation.
The AppMap runtime JAR must be appended to the bootstrap class loader
search path. This ensures that core AppMap runtime classes, such as
`HookFunctions`, are available to all application classes, regardless
of the specific class loader (e.g., in web servers like Tomcat). This
change fixes `NoClassDefFoundError` issues for `HookFunctions`.

This commit also adds a new `gretty-tomcat` test to verify the fix
in a servlet environment.
- Update `Agent.java` to use `Agent.class.getResource()` instead of
  `Agent.class.getClassLoader().getResource()` when locating the agent JAR.
  This prevents a `NullPointerException` when the agent is loaded by the
  bootstrap class loader (where `getClassLoader()` returns null).
- Modify `Properties.java` to automatically default `appmap.debug.disableGit`
  to `true` if the agent is running on the bootstrap classpath. This avoids
  crashes in JGit initialization, which relies on `ResourceBundle` loading
  that is problematic in the bootstrap context.
- Add a warning log in `Agent.premain` when running on the bootstrap
  classpath, advising that this configuration is for troubleshooting only.
Introduces a new configuration property `appmap.hooks.exclude` to allow
disabling specific AppMap hook classes by their fully qualified name.
This addresses issues where certain hooks, such as `SqlQuery`, might
cause `NoClassDefFoundError` due to classloading conflicts or
unexpected interactions with the target application's environment.

The new property can be set via a system property
`-Dappmap.hooks.exclude=<CLASS_NAME>` or an environment variable
`APPMAP_HOOKS_EXCLUDE=<CLASS_NAME>`.

The agent's `ClassFileTransformer` now checks this exclusion list
during hook processing, preventing the instrumentation of specified
hook classes.
This commit comprehensively refactors the AppMapPackage class to improve
readability, performance, and maintainability.

Replace linear exclusion matching with a PrefixTrie data structure,
reducing lookup complexity from O(N*M) to O(M), where N is the number of
exclusion patterns and M is the length of the class name. This provides
dramatic performance improvements for configurations with many exclusion
patterns.

Exclusion patterns can now be specified relative to the package path
(e.g., "internal" instead of "com.example.internal"), improving
configuration clarity while maintaining backward compatibility.

Add comprehensive documentation explaining the two mutually exclusive
configuration modes (exclude mode vs. methods mode). Refactor the complex
find() method into three clear helpers with explicit mode detection.

Add a warning when both 'exclude' and 'methods' are specified, making the
precedence rules explicit to users.

Enhance LabelConfig to support matching against both simple and fully
qualified class names for better user experience. Remove unused class
resolution logic.

Add 42 comprehensive tests covering both configuration modes, edge cases,
regex patterns, backward compatibility, and complex scenarios.

- Fix NullPointerException when 'exclude' field is empty in appmap.yml
- Fix package boundary matching (prevent "com.examples" matching "com.example")
- Remove unused 'allMethods' field (added in 2022, never implemented)
- Remove obsolete pattern threshold warning (no longer needed with PrefixTrie)
- Clean up unused imports
This commit refactors the SqlQuery hooks to provide comprehensive support
for JDBC operations, particularly PreparedStatements and batch operations.

Key changes:
- Refactored prepareStatement/prepareCall hooks to capture at METHOD_RETURN
  and associate SQL strings with the returned statement objects using
  WeakHashMaps for proper prepared statement support
- Added comprehensive batch operation tracking (addBatch, clearBatch,
  executeBatch, executeLargeBatch)
- Added support for executeLargeUpdate (JDBC 4.2)
- Consolidated overloaded methods using @ArgumentArray to reduce code
  duplication and handle all method signatures uniformly
- Added proper exception handling hooks for all execute methods
- Implemented caching of database names using WeakHashMap to avoid
  repeated metadata lookups
- Removed nativeSQL hooks (doesn't actually execute SQL, just transforms it)
- Fixed potential NoClassDefFoundError by changing exception handling from
  SQLException to Throwable and removing direct class references

The SQLException fix addresses an issue in environments with custom
classloaders (e.g., Oracle UCP) where java.sql.SQLException might not be
visible to the hook class's classloader.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit adds extensive test infrastructure for validating JDBC hook
behavior across multiple database systems.

Key additions:
- PureJDBCTests: Comprehensive unit tests covering all JDBC operations
  including Statement, PreparedStatement, CallableStatement, batch operations,
  and exception handling
- OracleRepositoryTests: Spring Data JPA integration tests for Oracle
- Test infrastructure supporting both H2 (in-memory) and Oracle databases
- Docker Compose configuration for running Oracle database in CI
- BATS test harness improvements and helper scripts for snapshot testing
- Snapshot-based test validation with expected SQL output for both databases
- CI integration: GitHub Actions workflow now includes Oracle database service
- Build configuration updated to include Oracle JDBC driver

Test utilities:
- helper.bash: Common test functions and database connection helpers
- regenerate_jdbc_snapshots.sh: Script to regenerate expected SQL snapshots
- Snapshot files for both H2 and Oracle to validate SQL generation

Also adds *.log pattern to .gitignore to prevent accidental commit of debug logs.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants